import { Router } from 'express';
import { Config } from '../objects/Config';
import { CustomRoute } from './customRoute';
import { join, resolve } from 'path';
import { readdirSync, lstatSync, existsSync } from 'fs';

export class ListingRoute implements CustomRoute {
    constructor(config: Config) {
        this.route = Router();
        this.config = config;
        this.assembleRoute();
    }

    public getRoute() {
        return this.route;
    }

    private assembleRoute() {
        this.route.get('/list', (req, res) => {
            const path = req.query['path'] || '/';
            if (typeof path !== 'string') {
                res.status(400).send({
                    code: -10400,
                    message: 'path must be a string'
                });
                return;
            }
            const content = this.listDirectory(path);
            res.send({
                code: 0,
                message: content
            });
        });

        this.route.get('/download', (req, res) => {
            const path = req.query['path'] || '/';
            if (typeof path !== 'string') {
                res.status(400).send({
                    code: -10400,
                    message: 'path must be a string'
                });
                return;
            }
            const content = this.resolveFile(path);
            if (content) {
                res.sendFile(content);
            } else {
                res.sendStatus(404);
            }
        });
    }

    private listDirectory(path: string) {
        return this.config.volumes.map(volume => {
            const actualPath = join(volume, this.toJailedPath(path));
            if (existsSync(actualPath)) {
                if (lstatSync(actualPath).isDirectory()) {
                    return readdirSync(actualPath);
                } else {
                    return [];
                }
            } else {
                return [];
            }
        }).reduce((a, v) => a.concat(v), []);
    }

    private toJailedPath(path: string) {
        return join('.', join('/', path));
    }

    private resolveFile(path: string): string | undefined {
        return this.config.volumes.map(volume => {
            const actualPath = join(volume, this.toJailedPath(path));
            if (existsSync(actualPath)) {
                if (lstatSync(actualPath).isFile()) {
                    return resolve(actualPath);
                } else {
                    return undefined;
                }
            } else {
                return undefined;
            }
        }).filter(it => typeof it === 'string')[0];
    }

    private readonly route: Router;
    private readonly config: Config;
}
